#include "typec_pd_timer.h"

//#define TYPEC_PD_TIMER_DBG
#ifdef TYPEC_PD_TIMER_DBG
#define TYPEC_TIMER_LOG(...)    printf(__VA_ARGS__)
static char __attribute__((unused)) *typec_timer_string[TYPEC_TIMER_END] = {
    [TYPEC_TIMER_CC_DEBOUNCE]    = "tCCDebounce",
    [TYPEC_TIMER_PD_DEBOUNCE]    = "tPDDebounce",
    [TYPEC_TIMER_TRYCC_DEBOUNCE] = "tTryCCDebounce",
    [TYPEC_TIMER_ERROR_RECOVERY] = "tErrorRecovery",
    [TYPEC_TIMER_SRC_DISCONNECT] = "tSrcDisconnect",
    [TYPEC_TIMER_DRP_TRY]        = "tDRPTry",
    [TYPEC_TIMER_DRP_TRYWAIT]    = "tDRPTryWait",
    [TYPEC_TIMER_TRY_TIMEOUT]    = "tTryTimeout",
    [TYPEC_TIMER_DRP_TRANSITION] = "tDRP",
};
static char __attribute__((unused)) *pd_timer_string[PD_TIMER_END - PD_TIMER_START] = {
    [PD_TIMER_PE_STARTUP -              PD_TIMER_START] = "PEStartup",
    [PD_TIMER_SENDER_RESPONSE -         PD_TIMER_START] = "SenderResponseTimer",
    [PD_TIMER_SOURCECAPABILITY -        PD_TIMER_START] = "SourceCapabilityTimer",
    [PD_TIMER_SINKWAITCAP -             PD_TIMER_START] = "SinkWaitCapTimer",
    [PD_TIMER_PSTRANSITION -            PD_TIMER_START] = "PSTransitionTimer",
    [PD_TIMER_PSSOURCEOFF -             PD_TIMER_START] = "PSSourceOffTimer",
    [PD_TIMER_PSSOURCEON -              PD_TIMER_START] = "PSSourceOnTimer",
    [PD_TIMER_NORESPONSE -              PD_TIMER_START] = "NoResponseTimer",
    [PD_TIMER_BISTCONTMODE -            PD_TIMER_START] = "BISTContModeTimer",
    [PD_TIMER_SWAPSOURCESTART -         PD_TIMER_START] = "SwapSourceStartTimer",
    [PD_TIMER_PSHARDRESET -             PD_TIMER_START] = "PSHardResetTimer",
    [PD_TIMER_VDMRESPONSE -             PD_TIMER_START] = "VDMResponseTimer",
    [PD_TIMER_DISCOVERIDENTITY -        PD_TIMER_START] = "DiscoverIdentityTimer",
    [PD_TIMER_SINKTX -                  PD_TIMER_START] = "SinkTxTimer",
    [PD_TIMER_CHUNKINGNOTSUPPORTED -    PD_TIMER_START] = "ChunkingNotSupportedTimer",
    [PD_TIMER_SINKPPSPERIODIC -         PD_TIMER_START] = "SinkPPSPeriodicTimer",
    [PD_TIMER_SRCRECOVER -              PD_TIMER_START] = "tSrcRecover",
    [PD_TIMER_SRCTRANSITION -           PD_TIMER_START] = "tSrcTransition",
    [PD_TIMER_SRCTRANSREQ -             PD_TIMER_START] = "tSrcTransReq",
    [PD_TIMER_VCONNSOURCEON -           PD_TIMER_START] = "tVconnSourceOn",
    [PD_TIMER_VCONNON -                 PD_TIMER_START] = "VconnOnTimer",
    [PD_TIMER_DELAY_START_DPM -         PD_TIMER_START] = "DelayStartDPM",
};
#else
#define TYPEC_TIMER_LOG(...)    ((void)0)
#endif

#define TYPEC_TIMER_BUFFER_MAX      4
#define PD_TIMER_BUFFER_MAX         4

static struct typec_timer_info typec_timer[TYPEC_TIMER_BUFFER_MAX];
static struct pd_timer_info pd_timer[PD_TIMER_BUFFER_MAX];

static bool is_timer_expired(uint32_t curr, uint32_t expiry)
{
    if (curr >= expiry) {
        if ((curr - expiry) < 0x7fffffff) {
            // normal case
            return true;
        } else {
            // in the case that expiry overflows
            return false;
        }
    } else {
        if ((expiry - curr) < 0x7fffffff) {
            // normal case
            return false;
        } else {
            // in the case that systick overflows
            return true;
        }
    }
}

void typec_pd_timer_init(void)
{
    memset(&typec_timer, 0, sizeof(struct typec_timer_info) * TYPEC_TIMER_BUFFER_MAX);
    memset(&pd_timer, 0, sizeof(struct pd_timer_info) * TYPEC_TIMER_BUFFER_MAX);
}

void typec_timer_enable(uint8_t timer, uint32_t timer_value)
{
    uint8_t i;
    uint32_t current = g_sys_ticks;

    /* Find first empty position */
    for (i = 0; i < TYPEC_TIMER_BUFFER_MAX; i++) {
        if (!typec_timer[i].valid) {
            break;
        }
    }

    typec_timer[i].valid = true;
    typec_timer[i].timer = timer;
    typec_timer[i].expiry = current + timer_value; // maybe overflow
#ifdef TYPEC_PD_TIMER_DBG
    TYPEC_TIMER_LOG("enable typec timer[%d] %s\n", i, typec_timer_string[timer]);
#endif
}

void typec_timer_disable(uint8_t timer)
{
    uint8_t i;

    for (i = 0; i < TYPEC_TIMER_BUFFER_MAX; i++) {
        if ((typec_timer[i].timer == timer) && typec_timer[i].valid) {
            typec_timer[i].valid = false;
            typec_timer[i].timer = TYPEC_PD_TIMER_NONE;
            typec_timer[i].expiry = 0;
#ifdef TYPEC_PD_TIMER_DBG
            TYPEC_TIMER_LOG("disable typec timer[%d] %s\n", i, typec_timer_string[timer]);
#endif
            break;
        }
    }
}

void typec_timer_disable_all(void)
{
    uint8_t i;

    for (i = 0; i < TYPEC_TIMER_BUFFER_MAX; i++) {
        typec_timer[i].valid = false;
        typec_timer[i].timer = TYPEC_PD_TIMER_NONE;
        typec_timer[i].expiry = 0;
    }
}

void pd_timer_enable(uint8_t timer, uint32_t timer_value, bool permanent)
{
    uint8_t i;
    uint32_t current = g_sys_ticks;

    /* Find first empty position */
    for (i = 0; i < PD_TIMER_BUFFER_MAX; i++) {
        if (!pd_timer[i].valid)
            break;
    }

    pd_timer[i].valid = true;
    pd_timer[i].timer = timer;
    pd_timer[i].expiry = current + timer_value; // maybe overflow
    pd_timer[i].permanent = permanent;
#ifdef TYPEC_PD_TIMER_DBG
    TYPEC_TIMER_LOG("enable pd timer[%d] %s\n", i, pd_timer_string[timer - PD_TIMER_START]);
#endif
}

void pd_timer_disable(uint8_t timer)
{
    uint8_t i;

    for (i = 0; i < PD_TIMER_BUFFER_MAX; i++) {
        if (pd_timer[i].timer == timer) {
            pd_timer[i].valid = false;
            pd_timer[i].timer = TYPEC_PD_TIMER_NONE;
            pd_timer[i].expiry = 0;
            pd_timer[i].permanent = false;
#ifdef TYPEC_PD_TIMER_DBG
            TYPEC_TIMER_LOG("disable pd timer[%d] %s\n", i, pd_timer_string[timer - PD_TIMER_START]);
#endif
            break;
        }
    }
}

void pd_timer_disable_all(void)
{
    uint8_t i;

    for (i = 0; i < PD_TIMER_BUFFER_MAX; i++) {
        pd_timer[i].valid = false;
        pd_timer[i].timer = TYPEC_PD_TIMER_NONE;
        pd_timer[i].expiry = 0;
        pd_timer[i].permanent = false;
    }
}

void pd_timer_disable_all_not_permanent(void)
{
    uint8_t i;

    for (i = 0; i < PD_TIMER_BUFFER_MAX; i++) {
        if (pd_timer[i].permanent)
            continue;
        pd_timer[i].valid = false;
        pd_timer[i].timer = TYPEC_PD_TIMER_NONE;
        pd_timer[i].expiry = 0;
    }
}

uint8_t typec_expired_timer(void)
{
    uint8_t i;
    uint8_t expired_timer;

    for (i = 0; i < TYPEC_TIMER_BUFFER_MAX; i++) {
        if (typec_timer[i].valid && (is_timer_expired(g_sys_ticks, typec_timer[i].expiry))) {
            typec_timer[i].valid = false;
            expired_timer = typec_timer[i].timer;
            typec_timer[i].timer = TYPEC_PD_TIMER_NONE;
            typec_timer[i].expiry = 0;
#ifdef TYPEC_PD_TIMER_DBG
            TYPEC_TIMER_LOG("typec timer %s expired\n", typec_timer_string[expired_timer]);
#endif
            return expired_timer;
        }
    }

    return TYPEC_PD_TIMER_NONE;
}

uint8_t pd_expired_timer(void)
{
    uint8_t i;
    uint8_t expired_timer;

    for (i = 0; i < PD_TIMER_BUFFER_MAX; i++) {
        if (pd_timer[i].valid && (is_timer_expired(g_sys_ticks, pd_timer[i].expiry))) {
            pd_timer[i].valid = false;
            expired_timer = pd_timer[i].timer;
            pd_timer[i].timer = TYPEC_PD_TIMER_NONE;
            pd_timer[i].expiry = 0;
            pd_timer[i].permanent = false;
#ifdef TYPEC_PD_TIMER_DBG
            TYPEC_TIMER_LOG("pd timer %s expired\n", pd_timer_string[expired_timer - PD_TIMER_START]);
#endif
            return expired_timer;
        }
    }

    return TYPEC_PD_TIMER_NONE;
}
